﻿using KinonekoSoftware.Foundation.UI.Controls.Charts;

using KinonekoSoftware.Foundation.UI.Media;

namespace KinonekoSoftware.Studio.Models.Modules
{
    public abstract class NumericGroupPresenterBase : GroupPresenter<Block_Numeric>, IGroupedPresenter
    {
        protected readonly Dictionary<string, NumericElementPresenter> Dictionary;

        protected NumericGroupPresenterBase(IModuleMessageBus queue)
        {
            Dictionary = new Dictionary<string, NumericElementPresenter>();
            Queue      = queue ?? throw new ArgumentNullException(nameof(queue));
            Items      = new ObservableCollection<NumericElementPresenter>();
        }

        public virtual NumericElementPresenter Select(Element_Numeric numeric) => null;


        public sealed override string GetTitle() => string.IsNullOrEmpty(Title) ? CodariaSR.Module_Block_Numeric : Title;
        public sealed override bool IsMultipleVariants() => true;
        public sealed override IEnumerable<Variant> GetVariants(string documentID) => Items.Select(x => x.GetVariant(documentID));
        public sealed override Variant GetVariant(string documentID) => null;

        #region IGroupedPresenter

        public IEnumerable<ModulePresenter> GetElements() => Items;

        public ModuleBlock GetBlock() => AssociatedObject;

        public ModulePresenter NewItem()
        {
            var item = new Element_Numeric
            {
                Id      = ID.Get(),
                Maximum = 100,
                Minimum = 0,
                Color   = "#007ACC",
            };
            var p = Select(item);

            AssociatedObject.Items.Add(item);
            Items.Add(p);
            return p;
        }

        public void ShiftUpItem(ModulePresenter target)
        {
            var target2 = (NumericElementPresenter)target;
            var element = target2.AssociatedObject;

            //
            //
            AssociatedObject.Items.ShiftUp(element);
            Items.ShiftUp(target2);
        }

        public void ShiftDownItem(ModulePresenter target)
        {
            var target2 = (NumericElementPresenter)target;
            var element = target2.AssociatedObject;

            //
            //
            AssociatedObject.Items.ShiftDown(element);
            Items.ShiftDown(target2);
        }

        public void RemoveItem(ModulePresenter target)
        {
            var target2 = (NumericElementPresenter)target;
            var element = target2.AssociatedObject;

            //
            //
            AssociatedObject.Items.Remove(element);
            Items.Remove(target2);
        }

        #endregion

        protected sealed override Block_Numeric CreateDummyInstance() => Block_Numeric.CreateInstance(NumericStyle.Degree);

        protected override Task<bool> OnEditItem() => null;

        protected override void OnIndexChanged(int oldIndex, int newIndex)
        {
            AssociatedObject.Index = newIndex;
        }


        public override void DataSync(Variant variant)
        {
            if (!string.IsNullOrEmpty(variant.Metadata) &&
                variant is StringVariant sv             &&
                int.TryParse(sv.Value, out var num)     &&
                Dictionary.TryGetValue(sv.Metadata, out var numeric))
            {
                // TODO: Maybe bug
                numeric.Value = num;
            }
        }


        protected Action OnMinMaxChanged { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int Maximum
        {
            get => AssociatedObject?.Maximum ?? 10;
            set
            {
                if (AssociatedObject is null)
                {
                    return;
                }

                if (Maximum < Minimum)
                {
                    AssociatedObject.Maximum = Minimum + 1;
                }

                AssociatedObject.Maximum = value;
                OnMinMaxChanged?.Invoke();
                RaiseUpdated();
            }
        }


        /// <summary>
        /// 
        /// </summary>
        public int Minimum
        {
            get => AssociatedObject?.Minimum ?? 0;
            set
            {
                if (AssociatedObject is null)
                {
                    return;
                }

                if (Maximum < Minimum)
                {
                    return;
                }

                AssociatedObject.Minimum = value;
                OnMinMaxChanged?.Invoke();
                RaiseUpdated();
            }
        }


        /// <summary>
        /// 
        /// </summary>
        public NumericStyle Style
        {
            get => AssociatedObject?.Style ?? NumericStyle.Progress;
            set
            {
                if (AssociatedObject is null)
                {
                    return;
                }

                AssociatedObject.Style = value;
                RaiseUpdated();
            }
        }

        public ObservableCollection<NumericElementPresenter> Items { get; }
    }

    public sealed class DegreeNumericGroupPresenter : NumericGroupPresenterBase
    {
        public DegreeNumericGroupPresenter(Block_Numeric target, IModuleMessageBus queue) : base(queue)
        {
            Initialize(target);
        }

        public override NumericElementPresenter Select(Element_Numeric numeric)
        {
            return new NumericElementPresenter
            {
                AssociatedObject = numeric,
                OnValueChanged   = OnValueChanged,
                Queue            = Queue,
                TopLevel         = this,
            };

        }


        private void OnValueChanged(string id, int value)
        {

        }

        protected override void OnInitialize(Block_Numeric target)
        {
            
            //
            // 清除
            Items.Clear();

            //
            // 添加
            foreach (var item in target.Items.Select(Select))
            {
                if (!string.IsNullOrEmpty(item.Metadata))
                {
                    Dictionary.TryAdd(item.Metadata, item);
                }

                Items.Add(item);
            }
        }


    }

    public sealed class ProgressNumericGroupPresenter : NumericGroupPresenterBase
    {

        public ProgressNumericGroupPresenter(Block_Numeric target, IModuleMessageBus queue) : base(queue)
        {
            Initialize(target);
        }


        private void OnValueChanged(string id, int value)
        {

        }

        public override NumericElementPresenter Select(Element_Numeric numeric)
        {
            return new NumericElementPresenter
            {
                AssociatedObject = numeric,
                OnValueChanged   = OnValueChanged,
                Queue            = Queue,
                TopLevel         = this,
            };

        }
        protected override void OnInitialize(Block_Numeric target)
        {
            //
            // 清除
            Items.Clear();

            //
            // 添加
            foreach (var item in target.Items.Select(Select))
            {
                if (!string.IsNullOrEmpty(item.Metadata))
                {
                    Dictionary.TryAdd(item.Metadata, item);
                }

                Items.Add(item);
            }
        }

    }

    public sealed class ChartNumericGroupPresenter : NumericGroupPresenterBase
    {
        private ChartPalette _palette;
        public ChartNumericGroupPresenter(Block_Numeric target, IModuleMessageBus queue) : base(queue)
        {
            SeriesMap = new Dictionary<string, Series>();
            Values    = new ChartSeriesCollection();
            Axes      = new ChartAxisCollection();
            Initialize(target);
            OnMinMaxChanged = OnMinMaxChangedOverride;
        }

        void OnMinMaxChangedOverride()
        {
            var min = Minimum;
            var max = Maximum;
            Values.Minimum = min;
            Values.Maximum = max;

            Items.ForEach(x =>
            {
                x.Maximum = max;
                x.Minimum = min;
            });
        }


        private void OnValueChanged(string id, int value)
        {
            if (SeriesMap.TryGetValue(id, out var s))
            {
                s.Value = value;
            }
        }

        protected override async Task<bool> OnEditItem()
        {
            var count = Items.Count;
            var r     = await base.OnEditItem();


            if (r)
            {
                Palette = ChartPalette.Create(MediaFactory.ToColor("#A9A9A9"), Items.Select(x => MediaFactory.ToColor(x.Color)));


                if (count != Items.Count)
                {
                    OnInitialize(AssociatedObject);
                }
            }

            return r;
        }

        public override NumericElementPresenter Select(Element_Numeric numeric)
        {
            return new NumericElementPresenter
            {
                AssociatedObject = numeric,
                OnValueChanged   = OnValueChanged,
                Queue            = Queue,
                TopLevel         = this,
            };

        }

        protected override void OnInitialize(Block_Numeric target)
        {
            Items.Clear();
            Values.Clear();
            SeriesMap.Clear();
            Axes.Clear();

            var chart = new ChartSeries
            {
                Maximum = target.Maximum,
                Minimum = target.Minimum,
            };

            
            //
            // 清除
            Items.Clear();

            foreach (var item in target.Items)
            {
                var series = new Series
                {
                    Id    = item.Id,
                    Value = item.Value,

                };

                var p = Select(item);
                p.Parent = this;
                
                if (!string.IsNullOrEmpty(item.Metadata))
                {
                    Dictionary.TryAdd(item.Metadata, p);
                }

                SeriesMap.Add(item.Id, series);
                Items.Add(p);
                chart.Add(series);
                Axes.Add(new ChartAxis { Name = item.Title });
            }

            Values.Add(chart);
            Palette = ChartPalette.Create("#808080", Items.FirstOrDefault()?.Color);
        }

        public Dictionary<string, Series> SeriesMap { get; }
        public ChartSeriesCollection      Values    { get; }
        public ChartAxisCollection        Axes      { get; }



        public ChartPalette Palette
        {
            get => _palette;
            set => SetValue(ref _palette, value);
        }

    }
}